/*
 *  $Id$
 *  Copyright [2010] [Panxiaobo] 
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License. 
 *  You may obtain a copy of the License at
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 *  Unless required by applicable law or agreed to in writing, software 
 *  distributed under the License is distributed on an "AS IS" BASIS, 
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 *  See the License for the specific language governing permissions and 
 *  limitations under the License. 
 */
package pxb.openid.op;

import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pxb.openid.Association;
import pxb.openid.Constants;
import pxb.openid.Forward;
import pxb.openid.OpenIdException;
import pxb.openid.util.Mac;
import pxb.openid.util.Util;

/**
 * 生成验证回复
 * 
 * @author Panxiaobo
 * 
 */
public class AuthenticationResponse implements Constants {
    /**
     * 默认字符集: UTF-8
     */
    private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    /**
     * 默认时区: UTC
     */
    private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC");

    private static final Logger log = LoggerFactory.getLogger(AuthenticationResponse.class);

    private SecureRandom random = new SecureRandom();

    /**
     * 生成一个验证取消回复
     * 
     * @param retTo
     * @return
     */
    public Forward generateCancelAuth(String retTo) {
        Map<String, String> map = new HashMap<String, String>();
        map.put(OPENID_NS, OPENID_20_NAMESPACE);
        map.put(OPENID_MODE, "cancel");
        if (log.isDebugEnabled()) {
            log.debug("OP -=> RP auth response:");
            Map<String, String> map1 = new TreeMap<String, String>(map);
            for (Map.Entry<String, String> entry : map1.entrySet()) {
                log.debug("{} = {}", entry.getKey(), entry.getValue());
            }
            log.debug("==================== End");
        }
        return new Forward(retTo, map);
    }

    /**
     * 生成一个验证错误回复
     * 
     * @param ret
     * @param msg
     *            错误信息
     * @return
     * @throws OpenIdException
     *             ret不存在时
     */
    public Forward generateErrorAuth(String ret, String msg) throws OpenIdException {
        if (ret == null || "".equals(ret)) {
            throw new OpenIdException(msg);
        }
        if (msg == null) {
            msg = "unknow error";
        }
        Map<String, String> map = new HashMap<String, String>();
        map.put(OPENID_NS, OPENID_20_NAMESPACE);
        map.put(OPENID_MODE, "error");
        map.put("openid.error", msg);
        if (log.isDebugEnabled()) {
            log.debug("OP -=> RP auth response:");
            Map<String, String> map1 = new TreeMap<String, String>(map);
            for (Map.Entry<String, String> entry : map1.entrySet()) {
                log.debug("{} = {}", entry.getKey(), entry.getValue());
            }
            log.debug("==================== End");
        }
        return new Forward(ret, map);
    }

    /**
     * 生成一个验证成功回复 10.1. Responding to Authentication Requests
     * 
     * @param serverURL
     *            OP的服务器URL
     * @param association
     * @param ret
     *            回复地址(RP的地址)
     * 
     * @param value
     *            其他数据
     */
    public Forward generateSuccessAuth(String serverURL, Association association, String ret, Map<String, String> value) {
        Map<String, String> map = new HashMap<String, String>();
        if (value != null)
            map.putAll(value);
        map.put(OPENID_NS, OPENID_20_NAMESPACE);
        map.put(OPENID_MODE, "id_res");
        map.put(OPENID_OP_ENDPOINT, serverURL);
        // map.put("openid.claimed_id", "a");
        // map.put("openid.identity", "a");
        map.put(OPENID_RETURN_TO, ret);

        // 发送服务器时区
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        sdf.setTimeZone(DEFAULT_TIMEZONE);
        map.put(OPENID_NONCE, sdf.format(new Date()) + Integer.toHexString(random.nextInt()));

        // map.put("openid.invalidate_handle", ret);
        map.put(OPENID_ASSOCIATION_HANDLE, association.getId());

        StringBuilder sbSigned = new StringBuilder();
        StringBuilder sbText = new StringBuilder();

        map.remove(OPENID_SIGNED);
        map.remove(OPENID_SIG);
        // 构造openid.signed和待签名字符串
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String name = entry.getKey();
            if (name.startsWith("openid.")) {
                String s = name.substring(7);
                sbSigned.append(',').append(s);
                sbText.append(s).append(':').append(entry.getValue()).append("\n");
            }
        }
        if (sbSigned.length() > 0)
            sbSigned.deleteCharAt(0);
        map.put(OPENID_SIGNED, sbSigned.toString());

        // 签名
        byte[] mac = association.getMac();
        String assoType = association.getType();
        byte[] sig;
        if (Constants.HMAC_SHA1.equals(assoType)) {
            sig = Mac.hmacSha1(mac, sbText.toString().getBytes(DEFAULT_CHARSET));
        } else {
            sig = Mac.hmacSha256(mac, sbText.toString().getBytes(DEFAULT_CHARSET));
        }
        String sigString = Util.ToBase64(sig);
        map.put(OPENID_SIG, sigString);

        // debug输出数据
        if (log.isDebugEnabled()) {
            log.debug("OP -=> RP auth response:");
            Map<String, String> map1 = new TreeMap<String, String>(map);
            for (Map.Entry<String, String> entry : map1.entrySet()) {
                log.debug("{} = {}", entry.getKey(), entry.getValue());
            }
            log.debug("==================== End");
        }
        return new Forward(ret, map);
    }
}
